package com.atguigu.gulimall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.to.es.SkuEsmodel;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.search.constant.EsConstant;
import com.atguigu.gulimall.search.feign.ProductFeifnService;
import com.atguigu.gulimall.search.service.MallSearchService;
import com.atguigu.gulimall.search.vo.AttrVo;
import com.atguigu.gulimall.search.vo.SearchParam;
import com.atguigu.gulimall.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @program: gulimall
 * @description:
 * @author: liuxvru
 * @create: 2024-03-12 21:55
 **/
@Service
public class MallSearchServiceImpl implements MallSearchService {
    /**
     * 检索结果
     *
     * @param searchParam
     * @return
     */

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private ProductFeifnService productFeifnService;

    @Override
    public SearchResult search(SearchParam searchParam) {
        SearchResult result = null;

        SearchRequest searchRequest = buildSearchRequest(searchParam);
        try {
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            result = buildSearchResult(search, searchParam);
        } catch (IOException ignored) {

        }
        return result;
    }

    /**
     * 封装返回数据
     *
     * @param search
     * @return
     */
    private SearchResult buildSearchResult(SearchResponse search, SearchParam searchParam) {
        SearchResult result = new SearchResult();
        SearchHits hits = search.getHits();
        result.setTotal(hits.getTotalHits().value);
        result.setTotalPages((int) (hits.getTotalHits().value % EsConstant.PAGE_SIZE == 0 ? hits.getTotalHits().value / EsConstant.PAGE_SIZE : hits.getTotalHits().value / EsConstant.PAGE_SIZE + 1));
        result.setPageNum(searchParam.getPageNum());
        List<SkuEsmodel> product = new ArrayList<>();
        if (hits.getHits() != null) {
            for (SearchHit hit : hits.getHits()) {
                String sourceAsString = hit.getSourceAsString();
                SkuEsmodel poJo = JSON.parseObject(sourceAsString, SkuEsmodel.class);
                if (StringUtils.hasText(searchParam.getKeyword())) {
                    poJo.setSkuTitle(hit.getHighlightFields().get("skuTitle").getFragments()[0].string());
                }
                product.add(poJo);
            }
        }
        result.setProduct(product);
        Aggregations aggregations = search.getAggregations();
        ParsedLongTerms catalog_agg = aggregations.get("catalog_agg");
        List<? extends Terms.Bucket> catalog_aggBuckets = catalog_agg.getBuckets();
        ArrayList<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        for (Terms.Bucket bucket : catalog_aggBuckets) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            catalogVo.setCatalogId((Long) bucket.getKey());
            ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
            String keyAsString = catalog_name_agg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(keyAsString);
            catalogVos.add(catalogVo);
        }
        result.setCatalogVos(catalogVos);


        ParsedNested attr_agg = aggregations.get("attr_agg");
        ParsedLongTerms attrIdAgg = attr_agg.getAggregations().get("attr_id_agg");
        List<? extends Terms.Bucket> attr_aggBuckets = attrIdAgg.getBuckets();
        ArrayList<SearchResult.AttrVo> attrVos = new ArrayList<>();
        for (Terms.Bucket attrAggBucket : attr_aggBuckets) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId((Long) attrAggBucket.getKey());
            ParsedStringTerms attr_name_agg = attrAggBucket.getAggregations().get("attr_name_agg");
            attrVo.setAttrName(attr_name_agg.getBuckets().get(0).getKeyAsString());
            ParsedStringTerms attr_value_agg = attrAggBucket.getAggregations().get("attr_value_agg");
            ArrayList<String> attrValue = new ArrayList<>();
            for (Terms.Bucket bucket : attr_value_agg.getBuckets()) {
                attrValue.add(bucket.getKeyAsString());
            }
            attrVo.setAttrValue(attrValue);
            attrVos.add(attrVo);
        }
        result.setAttrs(attrVos);


        ParsedLongTerms brand_agg = aggregations.get("brand_agg");
        List<? extends Terms.Bucket> brand_aggBuckets = brand_agg.getBuckets();
        ArrayList<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket bucket : brand_aggBuckets) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId((Long) bucket.getKey());
            ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
            ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
            brandVo.setBrandImg(brand_img_agg.getBuckets().get(0).getKeyAsString());
            brandVo.setBrandName(brand_name_agg.getBuckets().get(0).getKeyAsString());
            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);

        ArrayList<Integer> integers = new ArrayList<>();
        for (int i = 1; i <= result.getTotalPages(); i++) {
            integers.add(i);
        }
        result.setPageNavs(integers);


        //###########################################################################
        if (searchParam.getAttrs() != null && !searchParam.getAttrs().isEmpty()) {
            //构建面包屑导航
            List<SearchResult.NavVo> navVos = searchParam.getAttrs().stream().map(attr -> {
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                String[] split = attr.split("_");
                navVo.setNavValue(split[1]);
                R attrInfo = productFeifnService.getAttrInfo(Long.valueOf(split[0]));
                System.out.println(attrInfo);
                if (attrInfo.getCode() != 0) return null;
                AttrVo poJo = R.toPoJo(attrInfo.get("attr"), AttrVo.class);
                System.out.println(poJo);
                navVo.setNavName(poJo.getAttrName());
                navVo.setLink(attr);
                return navVo;
            }).collect(Collectors.toList());
            result.setNavs(navVos);
        }


        return result;
    }

    /**
     * 搜索请求
     *
     * @param searchParam
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //###########################################################################
        //query查询
        /*
          模糊匹配
         */
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.hasText(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }
        if (searchParam.getCatalog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
        }
        if (searchParam.getBrandId() != null && !searchParam.getBrandId().isEmpty()) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
        }
        /*
          属性查询
         */

        if (searchParam.getAttrs() != null && !searchParam.getAttrs().isEmpty()) {
            for (String attr : searchParam.getAttrs()) {
                BoolQueryBuilder attrsQuery = QueryBuilders.boolQuery();
                String[] split = attr.split("_");
                String[] split1 = split[1].split(":");
                attrsQuery.must(QueryBuilders.termsQuery("attrs.attrId", split[0]));
                attrsQuery.must(QueryBuilders.termsQuery("attrs.attrValue", split1));
                NestedQueryBuilder attrs = QueryBuilders.nestedQuery("attrs", attrsQuery, ScoreMode.None);
                boolQueryBuilder.filter(attrs);
            }
        }
        /*
        库存
         */
        if (searchParam.getHasStock() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
        }

        if (StringUtils.hasText(searchParam.getSkuPrice())) {
            RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");
            String[] split = searchParam.getSkuPrice().split("_");
            if (split.length == 2) {
                skuPrice.gte(split[0]);
                skuPrice.lte(split[1]);
            } else if (split.length == 1 && searchParam.getSkuPrice().startsWith("_")) {
                skuPrice.lte(split[0]);
            } else if (split.length == 1 && searchParam.getSkuPrice().endsWith("_")) {
                skuPrice.gte(split[0]);
            }
            boolQueryBuilder.filter(skuPrice);
        }
        sourceBuilder.query(boolQueryBuilder);
        //###########################################################################
        //###########################################################################
        /*
          排序
         */
        if (StringUtils.hasText(searchParam.getSort())) {
            String[] split = searchParam.getSort().split("_");
            SortOrder asc = split[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;
            sourceBuilder.sort(split[0], asc);
        }
        /*
          分页
         */
        sourceBuilder.from(EsConstant.PAGE_SIZE * (searchParam.getPageNum() - 1));
        sourceBuilder.size(EsConstant.PAGE_SIZE);

        //###########################################################################
        //###########################################################################
        /*
         高亮
         */
        if (StringUtils.hasText(searchParam.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        //###########################################################################
        //###########################################################################
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
        TermsAggregationBuilder brand_name_agg = AggregationBuilders.terms("brand_name_agg").field("brandName").size(1);
        TermsAggregationBuilder brand_img_agg = AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1);
        brandAgg.subAggregation(brand_name_agg);
        brandAgg.subAggregation(brand_img_agg);
        sourceBuilder.aggregation(brandAgg);

        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
        catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        sourceBuilder.aggregation(catalog_agg);

        NestedAggregationBuilder nested = AggregationBuilders.nested("attr_agg", "attrs");
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(50);
        TermsAggregationBuilder attr_name_agg = AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(50);
        TermsAggregationBuilder attr_value_agg = AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50);
        attr_id_agg.subAggregation(attr_name_agg);
        attr_id_agg.subAggregation(attr_value_agg);
        nested.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(nested);
        //###########################################################################
        System.out.println(sourceBuilder);
        return new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
    }
}
